// Copyright 2017 Baidu Inc. All Rights Reserved.
// Author: Zhang Guohua (zhangguohua02@baidu.com)
//
// Description: Status reporter

#include "baidu_status_reporter.h"
#include "baidu_ca_scheduler.h"
#include "baidu_ca_network_socket.h"
#include "baidu_ca_adapter.h"
#include "baidu_iot_ota_manager.h"
#include "WiFiStackInterface.h"
#include "duer_log.h"
#include "rda_ccfg_api.h"

// interval for periodic report 5min
static const size_t PERIODIC_REPORT_INTERVAL = 300000;

extern int rda_ota_enable;

#ifndef SDK_VERSION
#define SDK_VERSION ("beta-v1.2.4")
#endif

namespace duer {

StatusReporter& StatusReporter::obtain() {
    static StatusReporter status_reporter;
    return status_reporter;
}

int StatusReporter::register_report_function(ReportFunc report_function) {
    if (report_function == NULL) {
        return -2; // the report_function should not be NULL
    }

    _report_functions_mutex.lock();
    if (_current_report_function_index >= REPORT_FUNCTION_MAX) {
        _report_functions_mutex.unlock();
        return -1; // the array is full
    }

    _report_functions[_current_report_function_index++] = report_function;
    _report_functions_mutex.unlock();
    return 0;
}

void StatusReporter::start() {
    _periodic_report_timer.start(PERIODIC_REPORT_INTERVAL);
}

void StatusReporter::stop() {
    DUER_LOGI("StatusReporter::stop\n");
    _periodic_report_timer.stop();
}

void StatusReporter::device_info_report() {
    Object values;

    if (rda_ota_enable) {
        // get the firmware info from flash, the info avaiable only when OTA enable
        struct os_info os_info;
        int ret = OTAManager::instance()->get_os_info(os_info);
        if (ret == 0) {
            values.putString("firmware_version", os_info.version);
        } else {
            values.putString("firmware_version", "unknown");
        }
    } else {
        values.putString("firmware_version", "unknown");
    }

    int hwversion = rda_ccfg_hwver();
    if (hwversion == 2) {
        values.putString("chip_version", "RDA5981U02");
    } else if (hwversion == 4) {
        values.putString("chip_version", "RDA5981U04");
    } else {
        DUER_LOGI("hwversion:%d", hwversion);
        values.putString("chip_version", "unknown");
    }

    values.putString("sdk_version", SDK_VERSION);
    // Note: hardcode the network type currently
    const char* network_type = "WiFi";
    values.putString("network_type", network_type);
    NetworkInterface* network = SocketAdapter::get_network_interface();

    if (network != NULL) {
        values.putString("mac", network->get_mac_address());

        // wifi network will report the ssid info
        if (strcmp(network_type, "WiFi") == 0) {
            WiFiStackInterface* wifi_stack = (WiFiStackInterface*)network;
            values.putString("ssid", wifi_stack->get_ssid());
        }
    } else { // if network is null, report value will be ?
        values.putString("mac", "unknown");
        // wifi network will report the ssid info
        if (strcmp(network_type, "WiFi") == 0) {
            values.putString("ssid", "unknown");
        }
    }

    Object device_info;
    device_info.putObject("device_info", values);

    int rs = Scheduler::instance().report(device_info);
    if (rs < 0) {
        DUER_LOGW("device_info report failed!!!");
    }
}

void StatusReporter::device_status_report() {
    Object values;
    _report_functions_mutex.lock();

    for (int i = 0; i < _current_report_function_index; ++i) {
        if (_report_functions[i]) {
            _report_functions[i](values);
        }
    }

    _report_functions_mutex.unlock();
    Object device_status;
    device_status.putObject("device_status", values);
    int rs = Scheduler::instance().report(device_status);

    if (rs < 0) {
        DUER_LOGW("device status report failed!!!");
    }
}

StatusReporter::StatusReporter() :
        _periodic_report_timer(this, &StatusReporter::device_status_report,
                           osTimerPeriodic),
        _current_report_function_index(0) {
    memset(_report_functions, 0, sizeof(_report_functions));
}

} // namespace duer

